/** file InstrumentPerc.cpp
    author belot.nicolas
*/

#include "InstrumentPerc.h"
#include "SampleFactory.h"
#include "ChangePitch.h"
#include <iostream>
#include <cmath>

namespace libtrckr {

InstrumentPerc::InstrumentPerc():Instrument(){
	_name=string();
	//_tune=string();
	//_partition=queue<INote>();
//	_samples=map<string, INote>();
}

InstrumentPerc::InstrumentPerc(string Name):_name(Name),Instrument(){
	//_tune=string();
	//_partition=queue<INote>();
//	_samples=map<string, INote>();
}

InstrumentPerc::InstrumentPerc(const InstrumentPerc & im):Instrument(im),
	_name(im._name), 
	_tune(im._tune){
	//_partition(im._partition),
	//in fact we need to copy samples and attack...
	//will do it later.
}


InstrumentPerc::~InstrumentPerc(){
	for(uint i=0;i< nb_string;i++){
		delete _partitions[i];
	}
}

string InstrumentPerc::getName() const {
	return _name;
}

DynArray<string> InstrumentPerc::getTuning() const{
	return DynArray<string>(1,_tune);
}

DynArray<string> InstrumentPerc::getAttacks() const {
	return attack;
}

void InstrumentPerc::setName( string Name){
	_name=Name;
}

void InstrumentPerc::setTuning( DynArray<string> tune){//only one tuning...
	_tune=tune[0];
	nb_string=1;
	
	_partitions = DynArray<queue<INote>*>(4,NULL);
	for(uint i=0;i<4;i++){
		_partitions[i] = new queue<INote>();
	}
}

void InstrumentPerc::setNote( string name, string file,float speed,short panning,short att,short where){
	//we create a new channel for each note
	INote in;
	in._note = Note(name,Duration::NOIRE,false);
	in.filename=file;
	in.speed=speed;
	in._panning=panning;
	in.attack=att;
	Splay s;
	s.sample=0;
	s.panning=panning;
	s.elapsed=0;
	s.duration=0;
	s.ratio=speed;
	current.insert(pair<string, Splay>(name, s ) );
	if(att >= 0 && att < nb_attack  && _samples[att]!=0)
		_samples[att]->insert(pair<string, INote>(name, in) );
}

void InstrumentPerc::setAttack(string **att){
	int cnt=0;
	while(att[cnt]!=0){
		cnt++;
	}

	attack=DynArray<string>(cnt,string());
	_samples=DynArray<map<string,INote>*>(cnt,NULL);

	for(uint i=0;i<cnt;i++){
		attack[i]= *att[i];
		_samples[i] = new map<string,INote>();
	}
	nb_attack=cnt;
}

void InstrumentPerc::setAttack(DynArray<string> att){
	attack=att;
	_samples=DynArray<map<string,INote>*>(att.size(),NULL);
	for(uint i=0;i<att.size();i++){
		_samples[i] = new map<string,INote>();
	}
	nb_attack=att.size();
}


Buffer InstrumentPerc::play( uint size){
	Buffer ret = Buffer(size);
	Buffer tmp = Buffer(size);
	ret.channels(2);
	for( map<string, Splay>::iterator it = current.begin(); it != current.end();it++){
		
		if((*it).second.elapsed > size){
			if((*it).second.sample!=0){
				naturalEffects(tmp,*(*it).second.sample,(*it).second.effects);
				//tmp << *(*it).second.sample;
				if((*it).second.sample->isEOF()){
					(*it).second.sample=0;
				}
			}
			(*it).second.elapsed-=size;
		}else{
			Buffer tret((*it).second.elapsed);
			if((*it).second.sample!=0)
				naturalEffects(tret,*(*it).second.sample,(*it).second.effects);
				//tmp.append((*it).second.sample->read((*it).second.elapsed) );
			//we look for another sample with the same name.
			uint minimum=-1;bool find=false;
			for(uint i=0;i<nb_string;i++){
			if(_partitions[i]->size()>0){
				INote t = _partitions[i]->front();
				if(t._note.name() == (*it).first){
					find=true;
					if(_samples[t.attack]->find(t._note.name())!=_samples[t.attack]->end()){
					//we should destroy current[i] ?
						(*it).second.sample = SampleFactory::newSample( (*_samples[t.attack])[t._note.name()].filename.c_str());
						(*it).second.volume=t._volume;
						(*it).second.effects=(*_samples[t.attack])[t._note.name()].playing;
						
						minimum=minimum<t._note.sampleLength()?minimum:t._note.sampleLength();
						Buffer tret2(size-(*it).second.elapsed);
						naturalEffects(tret2,*(*it).second.sample,(*it).second.effects);
						tmp+=tret+tret2;
						//tmp << *(*it).second.sample;
						if((*it).second.sample->isEOF()){
							(*it).second.sample=0;
						}
						int ltmp = size - ((*it).second.duration - (*it).second.elapsed);
						(*it).second.duration = t._note.sampleLength();
						(*it).second.elapsed = (*it).second.duration - ltmp;
					}
					
				}else{
					(*it).second.elapsed=0;
				}
			}
			}
			if(!find){ //we had to silent for a time.
				(*it).second.sample=0;
				int ltmp = size - ((*it).second.duration - (*it).second.elapsed);
				(*it).second.duration=minimum;
				(*it).second.elapsed = (*it).second.duration - ltmp;
				
			}
		}
		for(uint j=0;j<size;j++){
			ret.setChannel(0);
			ret[j]+=(tmp[j]*(*it).second.volume)*((*it).second.panning/100.0);
			ret.setChannel(1);
			ret[j]+=(tmp[j]*(*it).second.volume)*(1.0 - ((*it).second.panning/100.0));//reparition
		}
	}
	return ret;
}
 
Buffer& InstrumentPerc::play( Buffer& b){
	uint size = b.capacity();
	b.flush();
	Buffer tmp = Buffer(size);
	b.channels(2);
	for( map<string, Splay>::iterator it = current.begin(); it != current.end();it++){
		
		if((*it).second.elapsed > size){
			if((*it).second.sample!=0){
				naturalEffects(tmp,*(*it).second.sample,(*it).second.effects);
				if((*it).second.sample->isEOF()){
					(*it).second.sample=0;
				}
			}
			(*it).second.elapsed-=size;
		}else{
			Buffer tret((*it).second.elapsed);
			if((*it).second.sample!=0)
				naturalEffects(tret,*(*it).second.sample,(*it).second.effects);
			//we look for another sample with the same name.
			uint minimum=-1;bool find=false;
			for(uint i=0;i<nb_string;i++){
			if(_partitions[i]->size()>0){
				INote t = _partitions[i]->front();
				if(t._note.name() == (*it).first){
					find=true;
					if(_samples[t.attack]->find(t._note.name())!=_samples[t.attack]->end()){
					//we should destroy current[i] ?
						(*it).second.sample = SampleFactory::newSample( (*_samples[t.attack])[t._note.name()].filename.c_str());
						(*it).second.volume=t._volume;
						(*it).second.effects=(*_samples[t.attack])[t._note.name()].playing;
						minimum=minimum<t._note.sampleLength()?minimum:t._note.sampleLength();
						Buffer tret2(size-(*it).second.elapsed);
						naturalEffects(tret2,*(*it).second.sample,(*it).second.effects);
						
						if((*it).second.sample->isEOF()){
							(*it).second.sample=0;
						}
						int ltmp = size - ((*it).second.duration - (*it).second.elapsed);
						(*it).second.duration = t._note.sampleLength();
						(*it).second.elapsed = (*it).second.duration - ltmp;
						b+=tret+tret2;
					}
					
				}
			}else{
				(*it).second.elapsed=0;
			}
			}
			if(!find){ //we had to silent for a time.
				(*it).second.sample=0;
				int ltmp = size - ((*it).second.duration - (*it).second.elapsed);
				(*it).second.duration=minimum;
				(*it).second.elapsed = (*it).second.duration - ltmp;
				
			}
		}
		for(uint j=0;j<size;j++){
			b.setChannel(0);
			b[j]+=(tmp[j]*(*it).second.volume)*((*it).second.panning/100.0);
			b.setChannel(1);
			b[j]+=(tmp[j]*(*it).second.volume)*(1.0 - ((*it).second.panning/100.0));//reparition
		}
	}
	return b;
}

void InstrumentPerc::addNote(string name, Duration::note_duration_t dura, bool point , uint volume,short att,short where,DynArray<PlayingEffect*>* eff=0){
	INote in ;
	in._note = Note(name, dura,point);
	in._volume=pow(10.0, (0.0-volume/100.0)/20.0);
	in.attack=att;
	in.playing=eff;
	if(where>=0 && where < nb_string){
		_partitions[where]->push(in);
	}
}

void InstrumentPerc::addNote(Note n, uint volume,short att,short where,DynArray<PlayingEffect*>* eff=0){
	INote in;
	in._note=n;
	in._volume=pow(10.0, (0.0-volume/100.0)/20.0);
	in.attack=att;
	in.playing=eff;
	if(where>=0 && where < nb_string){
		_partitions[where]->push(in);
	}
}


bool InstrumentPerc::end() const{
	bool partend=true;
	for(uint i=0;i<nb_string;i++){
		if(_partitions[i]->size()!=0)
			partend=false;
	}
	return partend;
}
void InstrumentPerc::letRing(bool ringing){
	ring=ringing;
}
}

